Mapped Tuple Type
従来のMapped Typesに対し、keyof TのTが配列やtupleだった場合のmappingの挙動が異なる
docs内ではMapped types on tuples and arraysなどと呼んでいる
TypeScript v3.1
PR
docs
Tが型変数の時、keyof TのTが配列やtupleだった場合の挙動が変わった
以前は
keyof unknown[] := "pop" | "push" | "concat" | "join" |..
の様にArrayのmethod名のunion型だった
v3.1以降では、
keyof unknown[] = number
の様に、numberのみが対象になる
Tが配列の時のkeyof Tはやや特殊な扱いになる、ということを頭に入れておけば良いmrsekut.icon
今の時代に、「v3.0以前と異なる」と説明されても全く有用ではない
(比較する機会はないので)
だから、「Tが配列以外の時と比較して特殊」と理解すればいい
とはいえ、直感に沿うようにするための変更なので、さほど深く考える必要はない
上記のPR内のコード例(一部改変)
準備
code:ts
type Box<T> = { value: T };
type Boxified<T> = { P in keyof T: Box<TP> };
これら↓は、配列から配列への型レベルmapと考えられる
code:ts
type T1 = Boxified<string[]>;
// Box<string>[]
type T3 = Boxified<number, string?>;
// Box<number>, Box<string|undefined>?
type T4 = Boxified<[number, ...string[]]>;
// [Box<number>, ...Box<string>[]]
type T6 = Boxified<(string | undefined)[]>;
// Box<string | undefined>[]
これ↓ は少し特殊だが、ほぼ同じ
code:ts
type T2 = Boxified<readonly string[]>;
// readonly Box<string>[]
Box<readonly string[]>の可能性も無きにしもあらずだが、これは配列ではない
「配列から配列へ変換される」というルールに則ったものなので理解しやすいmrsekut.icon
これ↓は、Distributive Conditional Typesとの併用
code:ts
type T5 = Boxified<string[] | undefined>;
// Box<string>[] | undefined
ditributeされたあとに、mapが行われている
Tが型引数なのかどうかで結果は変わる
code:ts
type n1 = 1, 2, 3;
type n2t<T> = { k in keyof T: 4 };
type n2b = n2t<n1>;
// 4, 4, 4
type n2 = { k in keyof n1: 4 };
/**
* type n2 = {
* x: number: 4;
* 0: 4;
* 1: 4;
* 2: 4;
* length: 4;
* toString: 4;
* ... 32 more ...;
* at: 4;
* }
*/
keyof Tが、
Tが型引数で、
Tが配列/tuple
のときだけ、lengthとかを無視してnumberになるよ
という話ねmrsekut.icon
genericsで無い時は、v3.0以前と同様に、keyof 配列は
keyof unknown[] := "pop" | "push" | "concat" | "join" |..
のように解釈されるので、上の例の様にぐちゃぐちゃの型になる
あくまでもnumberになっていることに注意
だから、以下のようなコードが通ってしまう
code:ts
const t1: keyof n1 = 1; // ok.
const t6: keyof n1 = 6; // ok!?
上記のPRの直前にissueが出てる
[F<E> for E in T]のような新たなsyntaxを入れて同様のことをしようというやつ
実際は、こういった新たなsyntaxは入れずに同様のことを実現している
TypeScript 3.1 の Mapped Tuple Typeについて - Qiita
https://qiita.com/uhyo/items/da21e2b3c10c8a03952f#mapped-typeと配列型
関連する話
Mapped Typesに配列型を入れた時の挙動